home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 1 / Gold Medal Software Volume 1 (Gold Medal) (1994).iso / prog / tpwprog3.arj / UMOVE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-07-02  |  10.5 KB  |  353 lines

  1. {******************************************************************}
  2. {                                                                  }
  3. {     Mancala                                                      }
  4. {     Turbo Pascal for Windows                                     }
  5. {     Copyright (c) 1991 by Swan Software. All rights reserved.    }
  6. {                                                                  }
  7. {******************************************************************}
  8.  
  9. { umove.pas -- Legal-move generator for Mancala }
  10.  
  11. unit UMove;
  12.  
  13. interface
  14.  
  15. uses UGlobals, UEval;
  16.  
  17.  
  18. var
  19.  
  20. {- Set MaxMoveIndex to 0 before calling search.  After the search,
  21. this variable equals the largest value reached by MoveIndex--a
  22. debugging device for determining the amount of stack space used (the
  23. game's stack, that is, not the processor's. }
  24.  
  25.   MaxMoveIndex: Integer;
  26.  
  27.  
  28. procedure MoveGen(var Position: Boardrec; Level: Integer;
  29.   var MoveList: ListRec);
  30.  
  31. function CupsEmpty(var Gameboard: Board; FirstCup,
  32.   LastCup: CupIndex): Boolean;
  33.  
  34. procedure MakeMove(var Position: BoardRec; Level: Integer;
  35.   Move: OneMove; var Score: Integer);
  36.  
  37. function LegalMove(Themove: Onemove; MoveList: ListRec): Boolean;
  38.  
  39.  
  40. implementation
  41.  
  42. {- The following ListMoves procedure creates a list of legal moves
  43. for board cup index range from StartCup to EndCup. Returns MoveList
  44. fields equal to the first move of the MoveList on the MoveStack. If
  45. MoveList.Count = 0, then there are no legal moves for this player. This
  46. function is called by MoveGen. Do not call it directly.
  47.  
  48. Each move is associated with a Gameboard. Both the move and the
  49. resulting Gameboard are saved in MoveStack and BoardStack arrays by
  50. this routine. The Level is used to make each move and assign a
  51. preliminary score. Odd levels are for the computer's moves; even
  52. levels for human moves. The Gameboard parameter represents the
  53. current position.
  54.  
  55. Note: A stack overflow, although prevented, causes no error--the
  56. computer will just think that no more moves are possible. Obviously,
  57. this will affect the computer's ability to find the best move, but it
  58. won't cause the program to halt. }
  59.  
  60. procedure ListMoves(var Position: BoardRec; Level: Integer;
  61.   StartCup: CupIndex; EndCup: CupIndex; var MoveList: ListRec);
  62. var
  63.   I: Integer;
  64. begin
  65.   with Position, MoveList do
  66.   begin
  67.     FirstIndex := MoveIndex + 1;  { Index to first move of move list }
  68.     Count := 0;                   { Initialize count (empty list) }
  69.     if not Win then
  70.       for I := StartCup to EndCup do
  71.         if Gameboard[I] > 0 then
  72.           if MoveIndex < maxStack then
  73.           begin
  74.             Inc(MoveIndex); { Advance stack ptr }
  75.             {- Copy current Gameboard to BoardStack }
  76.             Move(Gameboard, BoardStack[MoveIndex], Sizeof(Board));
  77.             with MoveStack[MoveIndex] do
  78.             begin
  79.               Move := I;    { Save move }
  80.               BoardIndex := MoveIndex;  { Points to game board after move }
  81.               MakeMove(BoardStack[MoveIndex], Level, Move, Score);
  82.             end;
  83.             Inc(Count);
  84.           end;
  85.     LastIndex := FirstIndex + Count - 1;
  86.     {- Record highest MoveIndex value for debugging amount
  87.        of stack space used during search. }
  88.     if MoveIndex > MaxMoveIndex then
  89.       MaxMoveIndex := MoveIndex;
  90.   end;
  91. end;
  92.  
  93.  
  94. {- Sort moves in ascending order, from the lowest to the highest
  95. score, bringing the worst positions for human moves to the front of
  96. the move list. }
  97.  
  98. {- Reference: Quicksort algorithm from Algorithms+Data
  99. Structures=Programs by Niklaus Wirth, pg 79. L = MoveStack index of
  100. first move of MoveList; R = MoveStack index of last move. Assume R > L. }
  101.  
  102. procedure SortAscending(L, R: Integer);
  103. var
  104.   I, J: Integer; X, W: MoveRec;
  105. begin
  106.   I := L;
  107.   J := R;
  108.   X := MoveStack[(L + R) div 2];
  109.   repeat
  110.     while Movestack[I].Score < X.Score do Inc(I);
  111.     while X.Score < Movestack[J].Score do Dec(J);
  112.     if I <= J then
  113.     begin
  114.       W := Movestack[I];
  115.       Movestack[I] := Movestack[J];
  116.       Movestack[J] := W;
  117.       Inc(I);
  118.       Dec(J);
  119.     end;
  120.   until I > J;
  121.   if L < J then SortAscending(L, J);
  122.   if I < R then SortAscending(I, R);
  123. end;
  124.  
  125.  
  126. {- Sort moves in descending order, from the highest to the lowest
  127. score, bringing the best positions for computer moves to the front of
  128. the move list. }
  129.  
  130. procedure SortDescending(L, R: Integer);
  131. var
  132.   I, J: Integer; X, W: MoveRec;
  133. begin
  134.   I := L;
  135.   J := R;
  136.   X := MoveStack[(L + R) div 2];
  137.   repeat
  138.     while Movestack[I].Score > X.Score do Inc(I);
  139.     while X.Score > Movestack[J].Score do Dec(J);
  140.     if I <= J then
  141.     begin
  142.       W := Movestack[I];
  143.       Movestack[I] := Movestack[J];
  144.       Movestack[J] := W;
  145.       Inc(I);
  146.       Dec(J);
  147.     end;
  148.   until I > J;
  149.   if L < J then SortDescending(L, J);
  150.   if I < R then SortDescending(I, R);
  151. end;
  152.  
  153.  
  154. procedure MoveGen(var Position: BoardRec; Level: Integer;
  155.   var MoveList: ListRec);
  156.  
  157. {- Generate list of legal moves for this search level. To create a
  158. move list outside of the search procedure, set level to 1 for
  159. computer moves, 0 for human. MoveList.Count = 0 indicates there are no
  160. legal moves for this player. The Gameboard represents the current
  161. position either during play or a position reached during simulated
  162. play. The call to ListMoves generates the list of legal moves. After
  163. that, if there are at least two moves on the list, it is sorted by
  164. preliminary scores in ascending order for human moves and descending
  165. order for computer moves. }
  166.  
  167. begin
  168.   if Odd(Level) then
  169.   begin
  170.     ListMoves(Position, Level, CompFirstCup, CompLastCup, MoveList);
  171.     with MoveList do if Count > 1 then
  172.       SortDescending(FirstIndex, LastIndex);
  173.   end else
  174.   begin
  175.     ListMoves(Position, Level, HumanFirstCup, HumanLastCup, MoveList);
  176.     with MoveList do if Count > 1 then
  177.       SortAscending(FirstIndex, LastIndex);
  178.   end;
  179. end;
  180.  
  181.  
  182. {- Return true if all cups from FirstCup to LastCup are empty. }
  183.  
  184. function CupsEmpty(var Gameboard: Board;
  185.   FirstCup, LastCup: CupIndex): Boolean;
  186. var
  187.   I: Integer;
  188. begin
  189.   for I := FirstCup to LastCup do
  190.     if Gameboard[I] > 0 then
  191.     begin
  192.       CupsEmpty := false;
  193.       Exit;
  194.     end;
  195.   CupsEmpty := true;
  196. end;
  197.  
  198.  
  199. {- Move all stones from FirstCup to Lastcup into this Kalah. This
  200. action occurs only when the opposite side's cups are all empty. }
  201.  
  202. procedure MoveAllPebbles(var Gameboard: Board; FirstCup, LastCup,
  203.   Kalah: CupIndex);
  204. var
  205.   I, Pebbles: Integer;
  206. begin
  207.   Pebbles := 0;
  208.   for I := FirstCup to LastCup do
  209.   begin
  210.     Pebbles := Pebbles + Gameboard[I];
  211.     Gameboard[I] := 0;
  212.   end;
  213.   Gameboard[Kalah] := Gameboard[Kalah] + Pebbles;
  214. end;
  215.  
  216.  
  217. {- Make this move on the global game board. Assume that the move
  218. is legal. Returns GoAgain = true if player can make another move.
  219. Otherwise, returns GoAgain = false. Odd Levels = moves for computer.
  220. Even Levels = moves for opponent (human). }
  221.  
  222. procedure MakeMove(var Position: BoardRec; Level: Integer;
  223.   Move: OneMove; var Score: Integer);
  224. var
  225.   Cup: CupIndex;
  226.   Pebbles: Integer;
  227.   PlayerKalah: CupIndex;
  228.   OpponentKalah: CupIndex;
  229.   CaptureCup: CupIndex;
  230.   FirstCup: CupIndex;
  231.   LastCup: CupIndex;
  232.   CupWasEmpty: Boolean;
  233.   CapturedPebbles: Integer;
  234. begin
  235.  
  236. {- Initialize local variables }
  237.  
  238.   if Odd(Level) then
  239.   begin { Make move for computer }
  240.     PlayerKalah := CompKalah;
  241.     OpponentKalah := HumanKalah;
  242.     FirstCup := CompFirstCup;
  243.     LastCup := CompLastCup;
  244.   end else
  245.   begin { Make move for human }
  246.     PlayerKalah := HumanKalah;
  247.     OpponentKalah := CompKalah;
  248.     FirstCup := HumanFirstCup;
  249.     LastCup := HumanLastCup;
  250.   end;
  251.  
  252.  
  253. {- Make the move, distributing pebbles in a counter-clockwise direction. }
  254.  
  255.   with Position do
  256.   begin
  257.     Cup := Move;
  258.     Pebbles := Gameboard[Move];
  259.     Gameboard[Move] := 0;
  260.     while Pebbles > 0 do
  261.     begin
  262.       if Cup = MaxCupIndex then
  263.         Cup := 0
  264.       else
  265.         Inc(Cup);
  266.       if Cup <> OpponentKalah then  { Skip opponent's kalah }
  267.       begin
  268.         CupWasEmpty := Gameboard[Cup] = 0;
  269.         Inc(Gameboard[Cup]);
  270.         Dec(Pebbles);
  271.       end;
  272.     end;
  273.  
  274.  
  275. {- Check for go-again if last pebble drops into player's own kalah. }
  276.  
  277.     GoAgain := (Cup = PlayerKalah);
  278.  
  279.  
  280. {- Check for captures when player's last stone drops into one of
  281. the player's own empty cups and an opposite cup contains pebbbles. }
  282.  
  283.     if not GoAgain then
  284.       if CupWasEmpty then
  285.         if FirstCup <= Cup then
  286.           if Cup <= LastCup then
  287.           begin  { Capture }
  288.             CaptureCup := MaxCups - Cup;
  289.             CapturedPebbles := Gameboard[CaptureCup];
  290.             if CapturedPebbles > 0 then
  291.             begin
  292.               Gameboard[PlayerKalah] :=
  293.                 Gameboard[PlayerKalah] + CapturedPebbles + 1;
  294.               Gameboard[CaptureCup] := 0;
  295.               Gameboard[Cup] := 0;
  296.             end;
  297.           end;
  298.  
  299.  
  300. {- Check for special condition where either side's cups are all
  301. empty. In this case, unless the side that has gone out has won, the
  302. opposite side moves all his pebbles into his kalah and wins. }
  303.  
  304.     if CupsEmpty(Gameboard, CompFirstCup, CompLastCup) then
  305.     begin
  306.       if Gameboard[Compkalah] < PebblesDiv2 then
  307.         MoveAllPebbles(Gameboard, HumanFirstCup, HumanLastCup, HumanKalah);
  308.     end else
  309.     if CupsEmpty(Gameboard, HumanFirstCup, HumanLastCup) then
  310.     begin
  311.       if Gameboard[HumanKalah] < PebblesDiv2 then
  312.         MoveAllPebbles(Gameboard, CompFirstCup, CompLastCup, CompKalah);
  313.     end;
  314.   end;
  315.  
  316.  
  317. {- Evaluate resulting position. This must be done here so that the
  318. win field is properly set when the main program calls MakeMove. This
  319. has to be done anyway for each position, so it may as well be here. }
  320.  
  321.   Score := BoardValue(Position, Level);
  322.  
  323. end;
  324.  
  325.  
  326. {- Return true if move is one of the moves on this MoveList.
  327. Never call this function if MoveList.Count = 0. This function is used
  328. to restrict human to making legal moves. The computer never calls it
  329. during the search for its own best move. }
  330.  
  331. function LegalMove(TheMove: OneMove; MoveList: ListRec): Boolean;
  332. var
  333.   I: StackRange;
  334. begin
  335.   with MoveList do
  336.     for I := FirstIndex to LastIndex do
  337.       if TheMove = MoveStack[I].Move then
  338.       begin
  339.         LegalMove := true;
  340.         Exit;
  341.       end;
  342.   LegalMove := false;
  343. end;
  344.  
  345.  
  346. end.
  347.  
  348.  
  349. { ----------------------------------------------------------------
  350.   Copyright (c) 1991 by Swan Software. All rights reserved.
  351.   Revision 1.00    Date: 8/21/1991
  352.   ---------------------------------------------------------------- }
  353.